home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr26 / netprog.zip / NETPROG.TAR / tftp / nettcp.c < prev    next >
C/C++ Source or Header  |  1989-12-17  |  7KB  |  283 lines

  1. /*
  2.  * TFTP network handling for TCP/IP connection.
  3.  */
  4.  
  5. #include    "netdefs.h"
  6.  
  7. #include    <netinet/in.h>
  8. #include    <arpa/inet.h>
  9. #include    <signal.h>
  10. #include    <errno.h>
  11. extern int    errno;
  12.  
  13. #ifndef    CLIENT
  14. #ifndef    SERVER
  15. either CLIENT or SERVER must be defined
  16. #endif
  17. #endif
  18.  
  19. int    sockfd = -1;                /* fd for socket of server */
  20. char    openhost[MAXHOSTNAMELEN] = { 0 };    /* remember host's name */
  21.  
  22. extern int            traceflag;    /* TFTP variable */
  23.  
  24. extern struct sockaddr_in    tcp_srv_addr;    /* set by tcp_open() */
  25. extern struct servent        tcp_serv_info;    /* set by tcp_open() */
  26.  
  27. #ifdef    CLIENT
  28.  
  29. /*
  30.  * Open the network connection.  Client version.
  31.  */
  32.  
  33. int
  34. net_open(host, service, port)
  35. char    *host;        /* name of other system to communicate with */
  36. char    *service;    /* name of service being requested */
  37. int    port;        /* if > 0, use as port#, else use value for service */
  38. {
  39.     if ( (sockfd = tcp_open(host, service, port)) < 0)
  40.         return(-1);
  41.  
  42.     DEBUG2("net_open: host %s, port# %d",
  43.             inet_ntoa(tcp_srv_addr.sin_addr),
  44.             ntohs(tcp_srv_addr.sin_port));
  45.  
  46.     strcpy(openhost, host);        /* save the host's name */
  47.  
  48.     return(0);
  49. }
  50.  
  51. #endif    /* CLIENT */
  52.  
  53. /*
  54.  * Close the network connection.  Used by client and server.
  55.  */
  56.  
  57. net_close()
  58. {
  59.     DEBUG2("net_close: host = %s, fd = %d", openhost, sockfd);
  60.  
  61.     close(sockfd);
  62.  
  63.     sockfd = -1;
  64. }
  65.  
  66. /*
  67.  * Send a record to the other end.  Used by client and server.
  68.  * With a stream socket we have to preface each record with its length,
  69.  * since TFTP doesn't have a record length as part of each record.
  70.  * We encode the length as a 2-byte integer in network byte order.
  71.  */
  72.  
  73. net_send(buff, len)
  74. char    *buff;
  75. int    len;
  76. {
  77.     register int    rc;
  78.     short        templen;
  79.  
  80.     DEBUG1("net_send: sent %d bytes", len);
  81.  
  82.     templen = htons(len);
  83.     rc = writen(sockfd, (char *) &templen, sizeof(short));
  84.     if (rc != sizeof(short))
  85.         err_dump("writen error of length prefix");
  86.  
  87.     rc = writen(sockfd, buff, len);
  88.     if (rc != len)
  89.         err_dump("writen error");
  90. }
  91.  
  92. /*
  93.  * Receive a record from the other end.  Used by client and server.
  94.  */
  95.  
  96. int                /* return #bytes in packet, or -1 on EINTR */
  97. net_recv(buff, maxlen)
  98. char    *buff;
  99. int    maxlen;
  100. {
  101.     register int    nbytes;
  102.     short        templen;    /* value-result parameter */
  103.  
  104. again1:
  105.     if ( (nbytes = readn(sockfd, (char *) &templen, sizeof(short))) < 0) {
  106.         if (errno == EINTR) {
  107.             errno = 0;        /* assume SIGCLD */
  108.             goto again1;
  109.         }
  110.         err_dump("readn error for length prefix");
  111.     }
  112.     if (nbytes != sizeof(short))
  113.         err_dump("error in readn of length prefix");
  114.  
  115.     templen = ntohs(templen);        /* #bytes that follow */
  116.     if (templen > maxlen)
  117.         err_dump("record length too large");
  118.  
  119. again2:
  120.     if ( (nbytes = readn(sockfd, buff, templen)) < 0) {
  121.         if (errno == EINTR) {
  122.             errno = 0;        /* assume SIGCLD */
  123.             goto again2;
  124.         }
  125.         err_dump("readn error");
  126.     }
  127.     if (nbytes != templen)
  128.         err_dump("error in readn");
  129.  
  130.     DEBUG1("net_recv: got %d bytes", nbytes);
  131.  
  132.     return(nbytes);        /* return the actual length of the message */
  133. }
  134.  
  135. #ifdef    SERVER
  136.  
  137. struct sockaddr_in        tcp_cli_addr;    /* set by accept() */
  138.  
  139. /*
  140.  * Initialize the network connection for the server, when it has *not*
  141.  * been invoked by inetd.
  142.  */
  143.  
  144. net_init(service, port)
  145. char    *service;    /* the name of the service we provide */
  146. int    port;        /* if nonzero, this is the port to listen on;
  147.                overrides the standard port for the service */
  148. {
  149.     struct servent    *sp;
  150.  
  151.     /*
  152.      * We weren't started by a master daemon.
  153.      * We have to create a socket ourselves and bind our well-known
  154.      * address to it.
  155.      */
  156.  
  157.     bzero((char *) &tcp_srv_addr, sizeof(tcp_srv_addr));
  158.     tcp_srv_addr.sin_family      = AF_INET;
  159.     tcp_srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  160.  
  161.     if (service != NULL) {
  162.         if ( (sp = getservbyname(service, "tcp")) == NULL)
  163.             err_dump("net_init: unknown service: %s/tcp", service);
  164.         tcp_serv_info = *sp;            /* structure copy */
  165.  
  166.         if (port > 0)
  167.             tcp_srv_addr.sin_port = htons(port);
  168.                             /* caller's value */
  169.         else
  170.             tcp_srv_addr.sin_port = sp->s_port;
  171.                             /* service's value */
  172.     } else {
  173.         if (port <= 0) {
  174.                err_ret("tcp_open: must specify either service or port");
  175.                return(-1);
  176.         }
  177.         tcp_srv_addr.sin_port = htons(port);
  178.     }
  179.  
  180.     /*
  181.      * Create the socket and Bind our local address so that any
  182.      * client can send to us.
  183.      */
  184.  
  185.     if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  186.         err_dump("net_init: can't create stream socket");
  187.  
  188.     if (bind(sockfd, (struct sockaddr *) &tcp_srv_addr,
  189.                     sizeof(tcp_srv_addr)) < 0)
  190.         err_dump("net_init: can't bind local address");
  191.  
  192.     /*
  193.      * And set the listen parameter, telling the system that we're
  194.      * ready  to accept incoming connection requests.
  195.      */
  196.  
  197.     listen(sockfd, 5);
  198. }
  199.  
  200. /*
  201.  * Initiate the server's end.
  202.  * We are passed a flag that says whether or not we were started
  203.  * by a "master daemon," such as the inetd program under 4.3BSD.
  204.  * A master daemon will have already waited for a message to arrive
  205.  * for us, and will have already set up the connection to the client.
  206.  * If we weren't started by a master daemon, then we must wait for a
  207.  * client's request to arrive.
  208.  */
  209.  
  210. int
  211. net_open(inetdflag)
  212. int    inetdflag;    /* true if inetd started us */
  213. {
  214.     register int    newsockfd, childpid, nbytes;
  215.     int        clilen, on;
  216.  
  217.     on = 1;
  218.  
  219.     if (inetdflag) {
  220. #ifdef    BSD        /* assumes 4.3BSD inetd */
  221.         /*
  222.          * When we're fired up by inetd under 4.3BSD, file
  223.          * descriptors 0, 1 and 2 are sockets to the client.
  224.          */
  225.  
  226.         sockfd = 0;    /* descriptor for net_recv() to read from */
  227.  
  228.         return(0);    /* done */
  229.  
  230. #endif    /* BSD inetd specifics */
  231.     }
  232.  
  233.     /*
  234.      * For the concurrent server that's not initiated by inetd,
  235.      * we have to wait for a connection request to arrive,
  236.      * then fork a child to handle the client's request.
  237.      * Beware that the accept() can be interrupted, such as by
  238.      * a previously spawned child process that has terminated
  239.      * (for which we caught the SIGCLD signal).
  240.      */
  241.  
  242. again:
  243.     clilen = sizeof(tcp_cli_addr);
  244.     newsockfd = accept(sockfd, (struct sockaddr *) &tcp_cli_addr, &clilen);
  245.     if (newsockfd < 0) {
  246.         if (errno == EINTR) {
  247.             errno = 0;
  248.             goto again;    /* probably a SIGCLD that was caught */
  249.         }
  250.         err_dump("accept error");
  251.     }
  252.  
  253.     /*
  254.      * Fork a child process to handle the client's request.
  255.      * The parent returns the child pid to the caller, which is
  256.      * probably a concurrent server that'll call us again, to wait
  257.      * for the next client request to this well-known port.
  258.      */
  259.  
  260.     if ( (childpid = fork()) < 0)
  261.         err_dump("server can't fork");
  262.  
  263.     else if (childpid > 0) {        /* parent */
  264.         close(newsockfd);    /* close new connection */
  265.         return(childpid);    /* and return */
  266.     }
  267.  
  268.     /*
  269.      * Child process continues here.
  270.      * First close the original socket so that the parent
  271.      * can accept any further requests that arrive there.
  272.      * Then set "sockfd" in our process to be the descriptor that
  273.      * we are going to process.
  274.      */
  275.  
  276.     close(sockfd);
  277.     sockfd = newsockfd;
  278.  
  279.     return(0);        /* return to process the connection */
  280. }
  281.  
  282. #endif    /* SERVER */
  283.